/*
GanttProject is an opensource project management tool. License: GPL3
Copyright (C) 2005-2011 GanttProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package net.sourceforge.ganttproject.export;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.text.MessageFormat;

import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.document.DocumentManager;
import net.sourceforge.ganttproject.language.GanttLanguage;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.IJobManager;
import org.eclipse.core.runtime.jobs.Job;

public class WebPublisher {

    public static class Ftp {
        private final FTPClient ftpClient = new FTPClient();
        private boolean isLoggedIn;
        private boolean isConnected;

        public IStatus loginAndChangedir(DocumentManager.FTPOptions options) throws IOException {
            ftpClient.connect(options.getServerName().getValue());
            int reply = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                ftpClient.disconnect();
                return new Status(IStatus.ERROR, "net.sourceforge.ganttproject",
                        GanttLanguage.getInstance().getText("errorFTPConnection") + " Connection failed: " + ftpClient.getReplyString());
            }
            isConnected = true;
            if (!ftpClient.login(options.getUserName().getValue(), options.getPassword().getValue())) {
                ftpClient.logout();
                ftpClient.disconnect();
                return new Status(IStatus.ERROR, "net.sourceforge.ganttproject",
                        GanttLanguage.getInstance().getText("errorFTPConnection") + " Login failed: " + ftpClient.getReplyString());
            }
            isLoggedIn = true;
            ftpClient.enterLocalPassiveMode();
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                GPLogger.getLogger(WebPublisher.class).warning("Failed to enter passive mode on FTP server=" + options.getServerName()
                        + " Reply message:" + ftpClient.getReplyString());
                ftpClient.enterLocalActiveMode();
                if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                    return new Status(IStatus.ERROR, "net.sourceforge.ganttproject",
                            GanttLanguage.getInstance().getText("errorFTPConnection") + " Passive and active mode failed: " + ftpClient.getReplyString());
                }
            }
            String dirName = options.getDirectoryName().getValue();
            if (dirName == null) {
                dirName = "";
            }
            if (!dirName.isEmpty() && !ftpClient.changeWorkingDirectory(dirName)) {
                ftpClient.logout();
                ftpClient.disconnect();
                return new Status(IStatus.ERROR, "net.sourceforge.ganttproject",
                        GanttLanguage.getInstance().getText("errorFTPConnection")
                        + MessageFormat.format(" Change directory to {0} failed: ", dirName, ftpClient.getReplyString()));
            }
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            return Status.OK_STATUS;
        }

        IStatus put(File file) throws IOException {
            if (!ftpClient.storeFile(file.getName(), new BufferedInputStream(new FileInputStream(file)))) {
                return new Status(IStatus.ERROR, "net.sourceforge.ganttproject",
                        "Failed to write file=" + file.getName() + " server response=" + ftpClient.getReplyString());
            }
            return Status.OK_STATUS;
        }

        public void detach() throws IOException {
            if (isLoggedIn) {
                ftpClient.logout();
            }
            if (isConnected) {
                ftpClient.disconnect();
            }
        }
    }

    WebPublisher() {
    }

    public void run(final File[] exportFiles, final DocumentManager.FTPOptions options) {
        IJobManager jobManager = Job.getJobManager();
        IProgressMonitor monitor = jobManager.createProgressGroup();
        Job startingJob = new Job("starting") {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                monitor.beginTask("Publishing files on FTP", exportFiles.length);
                try {
                    final Ftp ftp = new Ftp();
                    IStatus status = ftp.loginAndChangedir(options);
                    if (!status.isOK()) {
                        GPLogger.log(status.getMessage());
                        return status;
                    }
                    for (int i = 0; i < exportFiles.length; i++) {
                        Job nextJob = createTransferJob(ftp, exportFiles[i]);
                        nextJob.setProgressGroup(monitor, 1);
                        nextJob.schedule();
                        nextJob.join();
                    }
                    Job finishingJob = new Job("finishing") {
                        @Override
                        protected IStatus run(IProgressMonitor monitor) {
                            monitor.done();
                            try {
                                ftp.detach();
                                return Status.OK_STATUS;
                            } catch (IOException e) {
                                GPLogger.log(e);
                                return Status.CANCEL_STATUS;
                            }
                        }
                    };
                    finishingJob.setProgressGroup(monitor, 0);
                    finishingJob.schedule();
                    finishingJob.join();
                } catch (IOException e) {
                    if (!GPLogger.log(e)) {
                        e.printStackTrace(System.err);
                    }
                } catch (InterruptedException e) {
                    if (!GPLogger.log(e)) {
                        e.printStackTrace(System.err);
                    }
                }
                return Status.OK_STATUS;
            }
        };
        startingJob.setProgressGroup(monitor, 0);
        startingJob.schedule();
    }

    private Job createTransferJob(final Ftp ftp, final File file) {
        Job result = new Job("transfer file "+file.getName()) {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                try {
                    IStatus ftpStatus = ftp.put(file);
                    if (!ftpStatus.isOK()) {
                        GPLogger.getLogger(WebPublisher.class).warning(ftpStatus.getMessage());
                        return ftpStatus;
                    }
                    monitor.worked(1);
                    return Status.OK_STATUS;
                } catch (IOException e) {
                    if (!GPLogger.log(e)) {
                        e.printStackTrace(System.err);
                    }
                    return Status.CANCEL_STATUS;
                }
                finally {
                }
            }
        };
        return result;
    }

}
